package cn.gddevon.beans.aop;

import java.util.*;

/**
 * 切面织入器
 */
public class AspectWeaver {
    /**
     * 拥有一个bean容器
     */
    private Map<String, Object> beanContainer;

    /**
     * 存放所有切面类
     */
    private List<Class<?>> aspects;

    public AspectWeaver(){

    }

    public AspectWeaver(Map<String, Object> objectMap, List<Class<?>> aspects){
        this.beanContainer = objectMap;
        this.aspects = aspects;
    }

    public List<Class<?>> getNeedAspects() {
        return aspects;
    }

    /**
     * 1.获取所有的切面类
     * 2.遍历容器里的类
     * 3.筛选出匹配类的切面aspect
     * 4.尝试进行Aspect的织入 生成动态代理对象
     */
    public void doAop() throws InstantiationException, IllegalAccessException {
        //1.获取所有的切面类 needAspects
        List<Class<?>> needAspects = getNeedAspects();
        if(needAspects.isEmpty()){
            return;
        }
        //2.拼装AspectInfoList，代理链
        List<AspectInfo> aspectInfoList = packAspectInfoList(needAspects);
        //3.遍历容器里的类
        Set<Map.Entry<String, Object>> entries = beanContainer.entrySet();
        for (Map.Entry entry: entries) {
            Class<?> targetClass = entry.getValue().getClass();
            //4.粗筛符合条件的Aspect（此处只判断到类，不判断方法是否符合条件）
            List<AspectInfo> roughMatchedAspectList  = collectRoughMatchedAspectListForSpecificClass(aspectInfoList, targetClass);
            //5.尝试进行Aspect的织入
            String beanName = entry.getKey().toString();
            wrapIfNecessary(roughMatchedAspectList,targetClass,beanName);
        }
    }

    private void wrapIfNecessary(List<AspectInfo> roughMatchedAspectList, Class<?> targetClass, String beanName) {
        if(roughMatchedAspectList.isEmpty()){
            return;
        }
        //创建动态代理对象，此处用目标类对象和粗筛符合条件的aspect进行增强
        AspectListExecutor aspectListExecutor = new AspectListExecutor(targetClass, roughMatchedAspectList);
        Object proxyBean = ProxyCreator.createProxy(targetClass, aspectListExecutor);
        // 替换掉容器中未增强的类
        beanContainer.put(beanName, proxyBean);
    }

    private List<AspectInfo> collectRoughMatchedAspectListForSpecificClass(List<AspectInfo> aspectInfoList, Class<?> targetClass) {
        List<AspectInfo> roughMatchedAspectList = new ArrayList<>();
        for(AspectInfo aspectInfo : aspectInfoList){
            //粗筛
            if(aspectInfo.getPointcutLocator().roughMatches(targetClass)){
                roughMatchedAspectList.add(aspectInfo);
            }
        }
        return roughMatchedAspectList;
    }

    private List<AspectInfo> packAspectInfoList(List<Class<?>> aspectSet) throws InstantiationException, IllegalAccessException {
        List<AspectInfo> aspectInfoList = new ArrayList<>();
        for(Class<?> aspectClass : aspectSet){
            if (verifyAspect(aspectClass)){
                Order orderTag = aspectClass.getAnnotation(Order.class);
                Aspect aspectTag = aspectClass.getAnnotation(Aspect.class);
                DefaultAspect defaultAspect = (DefaultAspect) aspectClass.newInstance();
                //初始化表达式定位器
                PointcutLocator pointcutLocator = new PointcutLocator(aspectTag.pointcut());
                AspectInfo aspectInfo = new AspectInfo(orderTag.value(), defaultAspect, pointcutLocator);
                aspectInfoList.add(aspectInfo);
            } else {
                //不遵守规范则直接抛出异常
                throw new RuntimeException("@Aspect and @Order must be added to the Aspect class, and Aspect class must extend from DefaultAspect");
            }
        }
        return aspectInfoList;
    }

    //框架中一定要遵守给Aspect类添加@Aspect和@Order标签的规范，同时，必须继承自DefaultAspect.class
    //此外，@Aspect的属性值不能是它本身
    private boolean verifyAspect(Class<?> aspectClass) {
        return aspectClass.isAnnotationPresent(Aspect.class) &&
                aspectClass.isAnnotationPresent(Order.class) &&
                DefaultAspect.class.isAssignableFrom(aspectClass);
    }
}
